/*
 * Copyright (c) 2006-2021, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2022-02-27     chengjili       the first version
 */

#include <rtthread.h>
#include <rtdevice.h>

#if defined(BSP_USING_USER_ADC1) || defined(BSP_USING_USER_ADC2)

#define DRV_DEBUG
#define LOG_TAG             "user.adc"
#include <drv_log.h>

#define ADC1_BUF1_UPDATE_EVENT      0x00000001
#define ADC1_BUF2_UPDATE_EVENT      0x00000002

#define ADC_VREFINT_VAL             1497.89

#ifdef  BSP_USING_USER_ADC1
    user_adc_config adc1_config =
    {
        .ADC_Handler = ADC1,
        .name        = "user_adc1",
        .RCC_APB2_PERIPH_GPIOX = RCC_APB2_PERIPH_GPIOC,
        .regular_channels = {
                {ADC_CH_18, RT_NULL, RT_NULL},      //rank1 Vrefint
                {ADC_CH_16, RT_NULL, RT_NULL},      //rank2 temp_cup
                {ADC_CH_6, GPIOC, GPIO_PIN_0},      //rank3 out_voltage
                {ADC_CH_7, GPIOC, GPIO_PIN_1}       //rank4 out_current
        }
    };
    uint16_t user_adc1_val_buf[2][USER_ADC1_AVE_N * USER_ADC1_REGULAR_CH];
    float adc1_ave_buf[USER_ADC1_REGULAR_CH-1];
    rt_thread_t adc_data_handle_thread;
    rt_event_t adc_update_event;
#endif

#ifdef  BSP_USING_USER_ADC2
    user_adc_config adc2_config =
    {
        .ADC_Handler = ADC2,
        .name        = "user_adc2",
    };
#endif

void AdcDataHandleEntry(void *parameter);


rt_err_t user_adc_init(rt_device_t dev)
{
    GPIO_InitType GPIO_InitCtlStruct;
    GPIO_InitStruct(&GPIO_InitCtlStruct);
    RT_ASSERT(dev != RT_NULL);
    ADC_Module *ADCx = (ADC_Module *)dev->user_data;
    ADC_InitType ADC_InitStructure;

    adc_update_event = rt_event_create("adc_update", RT_IPC_FLAG_PRIO);

    if(adc_update_event != RT_NULL)
    {
        adc_data_handle_thread = rt_thread_create("adc_data_handle", AdcDataHandleEntry, RT_NULL, 2048, 1, 10);
        if(adc_data_handle_thread != RT_NULL)
            rt_thread_startup(adc_data_handle_thread);
    }

    #ifdef BSP_USING_USER_ADC1
    DMA_InitType ADC1_DMA_InitStructure;
    if(ADCx == ADC1)
    {
         /* ADC1 & GPIO clock enable */
         RCC_EnableAHBPeriphClk(RCC_AHB_PERIPH_ADC1 | RCC_AHB_PERIPH_DMA1, ENABLE);
         ADC_ConfigClk(ADC_CTRL3_CKMOD_AHB,RCC_ADCHCLK_DIV8);
         RCC_EnableAPB2PeriphClk(adc1_config.RCC_APB2_PERIPH_GPIOX, ENABLE);

         ADC_InitStruct(&ADC_InitStructure);
         ADC_InitStructure.WorkMode              = ADC_WORKMODE_INDEPENDENT;
         ADC_InitStructure.MultiChEn             = ENABLE;
         ADC_InitStructure.ContinueConvEn        = ENABLE;
         ADC_InitStructure.ExtTrigSelect         = ADC_EXT_TRIGCONV_NONE;
         ADC_InitStructure.DatAlign              = ADC_DAT_ALIGN_R;
         ADC_InitStructure.ChsNumber             = USER_ADC1_REGULAR_CH;
         ADC_Init(ADCx, &ADC_InitStructure);

         /* Configure ADC Channel as analog input */
         for(int i=0; i<USER_ADC1_REGULAR_CH; i++)
         {
             if(adc1_config.regular_channels[i].channel <= ADC_CH_11)
             {
                 GPIO_InitCtlStruct.Pin = adc1_config.regular_channels[i].GPIO_Pin;
                 GPIO_InitCtlStruct.GPIO_Speed = GPIO_Speed_2MHz;
                 GPIO_InitCtlStruct.GPIO_Mode = GPIO_Mode_AIN;
                 GPIO_InitPeripheral(adc1_config.regular_channels[i].GPIOX, &GPIO_InitCtlStruct);
             }
             /* ADCx regular channels configuration */
             ADC_ConfigRegularChannel(ADCx, adc1_config.regular_channels[i].channel, i+1, ADC_SAMP_TIME_239CYCLES5);

             if ((adc1_config.regular_channels[i].channel == ADC_CH_16) || (adc1_config.regular_channels[i].channel == ADC_CH_18))
             {
                 ADC_EnableTempSensorVrefint(ENABLE);
             }
         }

         /* Enable ADCx */
         ADC_Enable(ADCx, ENABLE);

         /* Start ADCx calibration */
         ADC_StartCalibration(ADCx);
         /* Check the end of ADCx calibration */
         while(ADC_GetCalibrationStatus(ADCx));

         ADC_Enable(ADCx, ENABLE);

         ADC1_DMA_InitStructure.BufSize = USER_ADC1_AVE_N * USER_ADC1_REGULAR_CH * 2;
         ADC1_DMA_InitStructure.CircularMode = DMA_MODE_CIRCULAR;
         ADC1_DMA_InitStructure.DMA_MemoryInc = DMA_MEM_INC_ENABLE;
         ADC1_DMA_InitStructure.Direction = DMA_DIR_PERIPH_SRC;
         ADC1_DMA_InitStructure.Mem2Mem = DMA_M2M_DISABLE;
         ADC1_DMA_InitStructure.MemAddr = (uint32_t)user_adc1_val_buf;
         ADC1_DMA_InitStructure.MemDataSize = DMA_MemoryDataSize_HalfWord;
         ADC1_DMA_InitStructure.PeriphAddr = &(ADCx->DAT);
         ADC1_DMA_InitStructure.PeriphDataSize = DMA_PERIPH_DATA_SIZE_HALFWORD;
         ADC1_DMA_InitStructure.PeriphInc = DMA_PERIPH_INC_DISABLE;
         ADC1_DMA_InitStructure.Priority = DMA_PRIORITY_MEDIUM;
         DMA_Init(DMA1_CH1, &ADC1_DMA_InitStructure);

         DMA_ConfigInt(DMA1_CH1, DMA_INT_HTX | DMA_INT_TXC, ENABLE);

         ADC_EnableDMA(ADCx, ENABLE);

         DMA_EnableChannel(DMA1_CH1, ENABLE);

         NVIC_SetPriorityGrouping(4);
         NVIC_EnableIRQ(DMA1_Channel1_IRQn);

         ADC_EnableSoftwareStartConv(ADCx, ENABLE);

    }
    #endif

    #ifdef BSP_USING_USER_ADC2
    if(ADCx == ADC2)
    {
         /* ADC2 & GPIO clock enable */
         RCC_EnableAHBPeriphClk(RCC_AHB_PERIPH_ADC2, ENABLE);
         ADC_ConfigClk(ADC_CTRL3_CKMOD_AHB,RCC_ADCHCLK_DIV8);
         RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_GPIOC, ENABLE);

         /* Configure ADC Channel as analog input */
         GPIO_InitCtlStruct.Pin = GPIO_PIN_1;
         GPIO_InitCtlStruct.GPIO_Speed = GPIO_Speed_2MHz;
         GPIO_InitCtlStruct.GPIO_Mode = GPIO_Mode_AIN;
         GPIO_InitPeripheral(GPIOC, &GPIO_InitCtlStruct);
    }
    #endif
    return RT_EOK;
}


rt_err_t user_adc_close(rt_device_t dev)
{
    ADC_Module *ADCx = (ADC_Module *)(dev->user_data);
    ADC_Enable(ADCx, DISABLE);
    if(ADCx == ADC1)
    {
        DMA_EnableChannel(DMA1_CH1, DISABLE);
        NVIC_DisableIRQ(DMA1_Channel1_IRQn);
        RCC_EnableAHBPeriphClk(RCC_AHB_PERIPH_ADC1 | RCC_AHB_PERIPH_DMA1, DISABLE);
    }
    rt_thread_delete(adc_data_handle_thread);
    rt_event_delete(adc_update_event);
    dev->flag &= ~(RT_DEVICE_FLAG_ACTIVATED);
    return RT_EOK;
}

static rt_size_t user_adc_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
{
    rt_size_t i;
    user_adc_device_t adc = (user_adc_device_t)dev;
    float *value = (float *)buffer;

    for (i = 0; i < size; i++)
    {
        if(pos+i < USER_ADC1_REGULAR_CH-1)
        {
            value[pos+i] = adc1_ave_buf[pos+i];
        }
        else {
            break;
        }
    }

    return i;
}

static rt_err_t user_adc_control(rt_device_t dev, int cmd, void *args)
{
    rt_err_t result = RT_EOK;
    user_adc_device_t adc = (user_adc_device_t)dev;


    return result;
}

rt_err_t user_hw_adc_register(user_adc_device_t device, const char *name, const void *user_data)
{
    rt_err_t result = RT_EOK;

    device->parent.type = RT_Device_Class_Miscellaneous;
    device->parent.rx_indicate = RT_NULL;
    device->parent.tx_complete = RT_NULL;

    device->parent.init        = user_adc_init;
    device->parent.open        = RT_NULL;
    device->parent.close       = user_adc_close;
    device->parent.read        = user_adc_read;
    device->parent.write       = RT_NULL;
    device->parent.control     = user_adc_control;

    device->parent.user_data = (void *)user_data;

    result = rt_device_register(&device->parent, name, RT_DEVICE_FLAG_RDONLY);

    return result;
}

static int user_hw_adc_init(void)
{
    int result = RT_EOK;
    /* register ADC device */
#ifdef  BSP_USING_USER_ADC1
    if (user_hw_adc_register(&adc1_config.n32_adc_device, adc1_config.name, adc1_config.ADC_Handler) == RT_EOK)
    {
        LOG_D("%s register success", adc1_config.name);
    }
    else
    {
        LOG_E("%s register failed", adc1_config.name);
        result = -RT_ERROR;
    }
#endif

#ifdef  BSP_USING_USER_ADC2
    if (user_hw_adc_register(&adc2_config.n32_adc_device, adc2_config.name, adc2_config.ADC_Handler) == RT_EOK)
    {
        LOG_D("%s register success", adc2_config.name);
    }
    else
    {
        LOG_E("%s register failed", adc2_config.name);
        result = -RT_ERROR;
    }
#endif

    return result;
}
INIT_COMPONENT_EXPORT(user_hw_adc_init);




void DMA1_Channel1_IRQHandler()
{
    if(DMA_GetIntStatus(DMA1_INT_HTX1,DMA1) == SET)
    {
        rt_event_send(adc_update_event, ADC1_BUF1_UPDATE_EVENT);
        DMA_ClrIntPendingBit(DMA1_INT_HTX1, DMA1);
    }
    if(DMA_GetIntStatus(DMA1_INT_TXC1,DMA1) == SET)
    {
        rt_event_send(adc_update_event, ADC1_BUF2_UPDATE_EVENT);
        DMA_ClrIntPendingBit(DMA1_INT_TXC1, DMA1);
    }
}

void GetAdcDataAverage(float *ave_buf, uint16_t *adc_buf)
{
    for(int i=0; i<USER_ADC1_REGULAR_CH-1; i++)
    {
        float adc_sum=0;
        for(int j=0; j<USER_ADC1_AVE_N; j++)
        {
            adc_sum += ADC_VREFINT_VAL * adc_buf[j*USER_ADC1_REGULAR_CH+i+1] / adc_buf[j*USER_ADC1_REGULAR_CH];
        }
        ave_buf[i] = adc_sum / USER_ADC1_AVE_N;
    }
}

void AdcDataHandleEntry(void *parameter)
{
    rt_uint32_t recved_event;
    while(1)
    {
        if(RT_EOK == rt_event_recv(adc_update_event, ADC1_BUF1_UPDATE_EVENT | ADC1_BUF2_UPDATE_EVENT, RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR, -1, &recved_event))
        {
            if(recved_event & ADC1_BUF1_UPDATE_EVENT)
            {
                GetAdcDataAverage(adc1_ave_buf, user_adc1_val_buf[0]);
            }
            if(recved_event & ADC1_BUF2_UPDATE_EVENT)
            {
                GetAdcDataAverage(adc1_ave_buf, user_adc1_val_buf[1]);
            }
        }
    }
}



#endif


